/*
 * Treeview 1.5pre - jQuery plugin to hide and show branches of a tree
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
 * http://docs.jquery.com/Plugins/Treeview
 *
 * Copyright (c) 2007 JÃ¶rn Zaefferer
 *
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
* Revision: $Id: jquery.treeview.js 5759 2008-07-01 07:50:28Z joern.zaefferer $
*
*/
(function($) {
// TODO rewrite as a widget, removing all the extra plugins
$.extend($.fn, {
								swapClass  : function(c1, c2) {
							var c1Elements = this.filter('.' + c1);
							this.filter('.' + c2).removeClass(c2).addClass(c1);
							c1Elements.removeClass(c1).addClass(c2);
							return this;
						},
						replaceClass : function(c1, c2) {
							return this.filter('.' + c1).removeClass(c1)
									.addClass(c2).end();
						},
						hoverClass : function(className) {
							className = className || "hover";
							return this.hover(function() {
								$(this).addClass(className);
							}, function() {
								$(this).removeClass(className);
							});
						},
						heightToggle : function(animated, callback) {
							animated ? this.animate({
								height : "toggle"
							}, animated, callback)
									: this
											.each(function() {
												jQuery(this)[jQuery(this).is(
														":hidden") ? "show"
														: "hide"]();
												if (callback)
													callback.apply(this,
															arguments);
											});
						},
						heightHide : function(animated, callback) {
							if (animated) {
								this.animate({
									height : "hide"
								}, animated, callback);
							} else {
								this.hide();
								if (callback)
									this.each(callback);
							}
						},
						prepareBranches : function(settings) {
							if (!settings.prerendered) {
								// mark last tree items
								this.filter(":last-child:not(ul)").addClass(
										CLASSES.last);
								// collapse whole tree, or only those marked as
								// closed, anyway except those marked as open
								this
										.filter(
												(settings.collapsed ? "" : "."
														+ CLASSES.closed)
														+ ":not(."
														+ CLASSES.open + ")")
										.find(">ul").hide();
							}
							// return all items with sublists
							return this.filter(":has(>ul)");
						},
						applyClasses : function(settings, toggler) {
							// TODO use event delegation
							this.filter(":has(>ul):not(:has(>a))")
									.find(">span").unbind("click.treeview")
									.bind("click.treeview", function(event) {
										// don't handle click events on
										// children, eg. checkboxes
										if (this == event.target)
											toggler.apply($(this).next());
									}).add($("a", this)).hoverClass();
							if (!settings.prerendered) {
								// handle closed ones first
								this.filter(":has(>ul:hidden)").addClass(
										CLASSES.expandable).replaceClass(
										CLASSES.last, CLASSES.lastExpandable);
								// handle open ones
								this.not(":has(>ul:hidden)").addClass(
										CLASSES.collapsable).replaceClass(
										CLASSES.last, CLASSES.lastCollapsable);
								// create hitarea if not present
								var hitarea = this.find("div."
										+ CLASSES.hitarea);
								if (!hitarea.length)
									hitarea = this.prepend(
											"<div class=\"" + CLASSES.hitarea
													+ "\"/>").find(
											"div." + CLASSES.hitarea);
								hitarea
										.removeClass()
										.addClass(CLASSES.hitarea)
										.each(
												function() {
													var classes = "";
													$
															.each(
																	$(this)
																			.parent()
																			.attr(
																					"class")
																			.split(
																					" "),
																	function() {
																		classes += this
																				+ "-hitarea ";
																	});
													$(this).addClass(classes);
												})
							}
							// apply event to hitarea
							this.find("div." + CLASSES.hitarea).click(toggler);
						},
						treeview : function(settings) {
							settings = $.extend({
								cookieId : "treeview"
							}, settings);
							if (settings.toggle) {
								var callback = settings.toggle;
								settings.toggle = function() {
									return callback.apply($(this).parent()[0],
											arguments);
								};
							}
							// factory for treecontroller
							function treeController(tree, control) {
								// factory for click handlers
								function handler(filter) {
									return function() {
										// reuse toggle event handler, applying
										// the elements to toggle
										// start searching for all hitareas
										toggler
												.apply($(
														"div."
																+ CLASSES.hitarea,
														tree)
														.filter(
																function() {
																	// for plain
																	// toggle,
																	// no filter
																	// is
																	// provided,
																	// otherwise
																	// we need
																	// to check
																	// the
																	// parent
																	// element
																	return filter ? $(
																			this)
																			.parent(
																					"."
																							+ filter).length
																			: true;
																}));
										return false;
									};
								}
								// click on first element to collapse tree
								$("a:eq(0)", control).click(
										handler(CLASSES.collapsable));
								// click on second to expand tree
								$("a:eq(1)", control).click(
										handler(CLASSES.expandable));
								// click on third to toggle tree
								$("a:eq(2)", control).click(handler());
							}
							// handle toggle event
							function toggler() {
								$(this).parent()
								// swap classes for hitarea
								.find(">.hitarea").swapClass(
										CLASSES.collapsableHitarea,
										CLASSES.expandableHitarea).swapClass(
										CLASSES.lastCollapsableHitarea,
										CLASSES.lastExpandableHitarea).end()
								// swap classes for parent li
								.swapClass(CLASSES.collapsable,
										CLASSES.expandable).swapClass(
										CLASSES.lastCollapsable,
										CLASSES.lastExpandable)
								// find child lists
								.find(">ul")
								// toggle them
								.heightToggle(settings.animated,
										settings.toggle);
								if (settings.unique) {
									$(this)
											.parent()
											.siblings()
											// swap classes for hitarea
											.find(">.hitarea")
											.replaceClass(
													CLASSES.collapsableHitarea,
													CLASSES.expandableHitarea)
											.replaceClass(
													CLASSES.lastCollapsableHitarea,
													CLASSES.lastExpandableHitarea)
											.end().replaceClass(
													CLASSES.collapsable,
													CLASSES.expandable)
											.replaceClass(
													CLASSES.lastCollapsable,
													CLASSES.lastExpandable)
											.find(">ul").heightHide(
													settings.animated,
													settings.toggle);
								}
							}
							this.data("toggler", toggler);
							function serialize() {
								function binary(arg) {
									return arg ? 1 : 0;
								}
								var data = [];
								branches.each(function(i, e) {
									data[i] = $(e).is(":has(>ul:visible)") ? 1
											: 0;
								});
								$.cookie(settings.cookieId, data.join(""),
										settings.cookieOptions);
							}
							function deserialize() {
								var stored = $.cookie(settings.cookieId);
								if (stored) {
									var data = stored.split("");
									branches
											.each(function(i, e) {
												$(e).find(">ul")[parseInt(data[i]) ? "show"
														: "hide"]();
											});
								}
							}
							// add treeview class to activate styles
							this.addClass("treeview");
							// prepare branches and find all tree items with child lists
							var branches = this.find("li").prepareBranches(
									settings);
							switch (settings.persist) {
							case "cookie":
								var toggleCallback = settings.toggle;
								settings.toggle = function() {
									serialize();
									if (toggleCallback) {
										toggleCallback.apply(this, arguments);
									}
								};
								deserialize();
								break;
							case "location":
								var current = this
										.find("a")
										.filter(
												function() {
													return this.href
															.toLowerCase() == location.href
															.toLowerCase();
												});
								if (current.length) {
									// TODO update the open/closed classes
									var items = current.addClass("selected")
											.parents("ul, li").add(
													current.next()).show();
									if (settings.prerendered) {
										// if prerendered is on, replicate the basic class swapping
										items
												.filter("li")
												.swapClass(CLASSES.collapsable,
														CLASSES.expandable)
												.swapClass(
														CLASSES.lastCollapsable,
														CLASSES.lastExpandable)
												.find(">.hitarea")
												.swapClass(
														CLASSES.collapsableHitarea,
														CLASSES.expandableHitarea)
												.swapClass(
														CLASSES.lastCollapsableHitarea,
														CLASSES.lastExpandableHitarea);
									}
								}
								break;
							}
							branches.applyClasses(settings, toggler);
							// if control option is set, create the treecontroller and show it
							if (settings.control) {
								treeController(this, settings.control);
								$(settings.control).show();
							}
							return this;
						}
					});
	// classes used by the plugin
	// need to be styled via external stylesheet, see first example
	$.treeview = {};
	var CLASSES = ($.treeview.classes = {
		open : "open",
		closed : "closed",
		expandable : "expandable",
		expandableHitarea : "expandable-hitarea",
		lastExpandableHitarea : "lastExpandable-hitarea",
		collapsable : "collapsable",
		collapsableHitarea : "collapsable-hitarea",
		lastCollapsableHitarea : "lastCollapsable-hitarea",
		lastCollapsable : "lastCollapsable",
		lastExpandable : "lastExpandable",
		last : "last",
		hitarea : "hitarea"
	});
})(jQuery);
